namespace Resharper.Abstractions.CodeDom.TypeMembers
{
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi.ExtensionsAPI;
  using JetBrains.ReSharper.Psi.Tree;
  using Resharper.Abstractions.CodeDom.Comments;
  using Resharper.Abstractions.CodeDom.TreeNodes;
  using Resharper.Abstractions.CodeDom.Types;

  /// <summary>Defines the <see cref="TypeMember"/> class.</summary>
  public class TypeMember : TreeNode
  {
    #region Constructors and Destructors

    /// <summary>Initializes a new instance of the <see cref="TypeMember"/> class.</summary>
    /// <param name="typeMember">The member.</param>
    internal TypeMember([NotNull] ITypeMemberDeclaration typeMember) : base(typeMember)
    {
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the access right.
    /// </summary>
    public AccessRights AccessRights
    {
      get
      {
        return AccessRightsHelper.GetAccessRights(this.InternalTypeMember.DeclaredElement);
      }
    }

    /// <summary>
    /// Gets the comment.
    /// </summary>
    [CanBeNull]
    public Comment Comment
    {
      get
      {
        var owner = this.InternalTreeNode as IDocCommentBlockOwnerNode;
        if (owner == null)
        {
          return null;
        }

        var node = SharedImplUtil.GetDocCommentBlockNode(owner);

        return node != null ? new Comment(node) : null;
      }

      set
      {
        if (value == null)
        {
          return;
        }

        var docCommentBlockOwnerNode = this.InternalTreeNode as IDocCommentBlockOwnerNode;
        if (docCommentBlockOwnerNode != null)
        {
          docCommentBlockOwnerNode.SetDocCommentBlockNode(value.InternalComment);
        }
      }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is abstract.
    /// </summary>
    /// <value><c>true</c> if this instance is abstract; otherwise, <c>false</c>.</value>
    public virtual bool IsAbstract
    {
      get
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;

        return member != null && member.IsAbstract;
      }

      set
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;
        if (member != null)
        {
          member.SetAbstract(value);
        }
      }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is extern.
    /// </summary>
    /// <value><c>true</c> if this instance is extern; otherwise, <c>false</c>.</value>
    public virtual bool IsExtern
    {
      get
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;

        return member != null && member.IsExtern;
      }

      set
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;
        if (member != null)
        {
          member.SetExtern(value);
        }
      }
    }

    /// <summary>
    /// Gets a value indicating whether this instance is virtual.
    /// </summary>
    public virtual bool IsVirtual
    {
      get
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;

        return member != null && member.IsVirtual;
      }

      set
      {
        var member = this.InternalTypeMember as IModifiersOwnerDeclaration;
        if (member != null)
        {
          member.SetVirtual(value);
        }
      }
    }

    /// <summary>
    /// Gets the name.
    /// </summary>
    [NotNull]
    public string Name
    {
      get
      {
        return this.InternalTypeMember.DeclaredName ?? string.Empty;
      }
    }

    /// <summary>
    /// Gets the name of the qualified.
    /// </summary>
    [NotNull]
    public string QualifiedName
    {
      get
      {
        var result = this.Name;
        var type = this.GetContainingType();

        if (type != null)
        {
          result = type.Name + "." + result;
        }

        return result;
      }
    }

    /// <summary>
    /// Gets the full name.
    /// </summary>
    [NotNull]
    public string FullName
    {
      get
      {
        var result = this.Name;
        var type = this.GetContainingType();

        if (type != null)
        {
          result = type.FullName + "." + result;
        }

        return result;
      }
    }

    /// <summary>The member field.</summary>
    [NotNull]
    internal ITypeMemberDeclaration InternalTypeMember
    {
      get
      {
        return (ITypeMemberDeclaration)this.InternalTreeNode;
      }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Gets the type of the get containing.
    /// </summary>
    [CanBeNull]
    public TypeElement GetContainingType()
    {
      var type = this.InternalTreeNode.GetContainingNode<ITypeDeclaration>();

      return type != null ? new TypeElement(type) : null;
    }

    #endregion
  }
}